//
// Created by 11510 on 2024/7/29.
//

#ifndef WEB_LIBRARY_PAGELIB_H
#define WEB_LIBRARY_PAGELIB_H

#include "DirScanner.h"
#include "tinyxml2.h"
#include <regex>
#include <vector>
#include <string>
#include <map>
#include <fstream>
#include <sstream>
#include <memory>

using namespace tinyxml2;

struct RssItem {
    std::string title;
    std::string link;
    std::string text;

    RssItem(const std::string &title, const std::string &link, const std::string &content) {
        this->title = std::move(title);
        this->link = std::move(link);
        this->text = std::move(content);
    }
};

class PageLib {
private:
    const DirScanner &dirScanner;
    std::shared_ptr<std::vector<RssItem>> rssPtr;/* 格式化后的网页 */
    std::map<int, std::pair<int, int>> offsetLib;/* 每篇文档在网页库的位置 */

public:
    PageLib(const DirScanner &dirScanner) :
            dirScanner(dirScanner) {
        rssPtr = std::make_shared<std::vector<RssItem>>();
        rssPtr->reserve(2048);
    }

//    创建xml网页库
    void create() {
        auto &filesList = dirScanner.getFiles();

        for (const auto &file: filesList) {
            std::ifstream ifs;
            ifs.open(file);
            if (!ifs) {
                std::cerr << "ifstream open file failed\n";
                return;
            }

            parseRss(file.c_str());

            ifs.close();
        }
    }

//    保存网页库，偏移库
    void store(const char *filename) {
//        const char *declaration = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>";
        XMLDocument result;
//        result.Parse(declaration);//会覆盖xml所有内容

        int cntDocumentId = 0;
//        XMLPrinter* printer = new XMLPrinter();

        for (auto it = rssPtr->begin(); it != rssPtr->end(); it++) {
            // 记录起始位置
//            printer->ClearBuffer();
//            result.Print(printer);
//            long long startPos = printer->CStrSize();

            XMLElement *doc = result.NewElement("doc");
            result.InsertEndChild(doc);
            insertChild(&result, doc, "docid", std::to_string(cntDocumentId).c_str());
            insertChild(&result, doc, "title", it->title.c_str());
            insertChild(&result, doc, "link", it->link.c_str());
            insertChild(&result, doc, "content", it->text.c_str());


            // 记录结束位置并计算长度
//            printer->ClearBuffer();
//            result.Print(printer);
//            long long endPos = printer->CStrSize();
//            long long length = endPos - startPos;

//            offsetLib[cntDocumentId] = std::make_pair(startPos, length);
//            printf("pos = %lld, len = %lld\n", startPos, length);
            cntDocumentId++;
        }

        result.SaveFile(filename);
    }

    void storeOffset(const std::string &filepath) {
        std::ofstream ofs(filepath);
        if (!ofs) {
            std::cerr << "ofstream open file failed\n";
            return;
        }

        for (const auto &entry: offsetLib) {
            ofs << entry.first << " "
                << entry.second.first << " "
                << entry.second.second << "\n";
        }
        ofs.close();
    }

private:
    void parseRss(const char *filename) {
        XMLDocument document;
        document.LoadFile(filename);
        XMLElement *root = document.RootElement();
        XMLElement *channel = root->FirstChildElement("channel");

        for (XMLElement *item = channel->FirstChildElement("item"); item; item = item->NextSiblingElement("item")) {

            XMLElement *title = item->FirstChildElement("title");
            XMLElement *link = item->FirstChildElement("link");

            XMLElement *text = item->FirstChildElement("description");
            if (nullptr == text) {
                text = item->FirstChildElement("content");
            }

            /* 此处有坑，需要踢出content为null的标签 */
            std::string trimText;
            if (text != nullptr && text->GetText() != nullptr) {
                trimText = std::regex_replace(text->GetText(), std::regex(R"(<[^>]+>)"), "");
            }

            if (trimText != "" && !isAllSpaces(trimText)) {
                rssPtr->push_back({title->GetText(), link->GetText(), trimText});
            }

        }
    }

    void insertChild(XMLDocument *page, XMLElement *parent, const char *elemName, const char *elemText) {
        XMLElement *element = page->NewElement(elemName);
        XMLText *elementText = page->NewText(elemText);
        element->InsertEndChild(elementText);
        parent->InsertEndChild(element);
    }

    bool isAllSpaces(const std::string& str) {
        // 使用 std::all_of 检查所有字符是否都是空格
        return std::all_of(str.begin(), str.end(), [](char c) { return c == ' '; });
    }
};


#endif //WEB_LIBRARY_PAGELIB_H
